package web

import (
	"encoding/json"
	"net/http"
	"strings"
	"sync"
	"text/template"

	"commonUtils/utils"
	"commonUtils/web/session"

	"github.com/gorilla/websocket"
)

// Msg 消息模型
type Msg struct {
	Action string      `json:"action"`
	Data   interface{} `json:"data"`
}

func CreateMsg(action string, data interface{}) *Msg {
	return &Msg{
		Action: action,
		Data:   data,
	}
}

func CreateMsgByte(action string, data interface{}) []byte {
	var msg = CreateMsg(action, data)

	jsonBytes, err := json.Marshal(msg)
	if err != nil {
		utils.Error("json parse error : %s, data : %+v", err, msg)
	}

	return jsonBytes
}

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024000,
	WriteBufferSize: 1024000,
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

// Server websocket服务器
type Server struct {
	*WsConfig
	IndexTemp *template.Template
	Sessions  *session.Manager

	Channels            map[string]*Channel
	Conns               []*Conn
	muConns, muChannels sync.RWMutex
}

var serverInst *Server

func ServerInst() *Server {
	return serverInst
}

func SessionStart(w http.ResponseWriter, r *http.Request) session.Session {
	if nil == serverInst {
		return nil
	}
	return serverInst.Sessions.SessionStart(w, r)
}

func DistorySession(sid string) error {
	if nil == serverInst {
		return nil
	}
	return serverInst.Sessions.DistorySessionBySID(sid)
}

// BuildServer 创建一个ws服务
func (b *WsBuilder) BuildServer() *Server {
	if serverInst != nil {
		return serverInst
	}
	server := &Server{
		WsConfig: b.wsConfig,
		Channels: make(map[string]*Channel),
	}
	var err error
	// t, err := template.ParseFiles(server.TempFiles...)

	// if err != nil {
	// 	utils.Error("parse index file err:%s", err)
	// 	return nil
	// }

	// server.IndexTemp = t

	server.Sessions, err = session.NewSessionManager("memory", "goSessionID", server.SessionExpire)
	if err != nil {
		utils.Error("New session manager error : %s", err)
	}
	go server.Sessions.GC()
	serverInst = server
	return server
}

// BuildConn 创建连接
func (b *WsBuilder) BuildConn(conn *websocket.Conn, server *Server, key string, clientIp string) *Conn {

	conn.SetReadLimit(b.wsConfig.ReadLimit)

	c := &Conn{
		conn:            conn,
		WsConfig:        *b.wsConfig,
		server:          server,
		ID:              key,
		writeBufferChan: make(chan []byte, b.wsConfig.WriteBufferChanCount),
		IP:              clientIp,
	}

	if nil != c.ConnectSuccessAfterSendMessage {
		message := c.ConnectSuccessAfterSendMessage(c)
		if len(message) > 0 {
			go func(m []byte) {
				c.writeBufferChan <- m
			}(message)
		}
	}
	if nil != c.OnConnected {
		c.OnConnected(c)
	}
	return c
}

// BuilderChannel 初始化频道
func (b *WsBuilder) BuilderChannel(name string) *Channel {
	utils.Info("Build Channel : %s", name)
	c := &Channel{
		WsConfig:  *b.wsConfig,
		Name:      name,
		broadcast: make(chan []byte),
		close:     make(chan bool),
		clients:   make(map[*Conn]bool),
	}
	go c.Process()

	return c
}

// Serv 启动服务
func (s *Server) Serv() {

	if s.StaticFolder != "" && s.StaticURL != "" {
		if !utils.EndWith(s.StaticURL, "/") {
			s.StaticURL += "/"
		}

		if len(s.StaticURL) > 0 {
			http.Handle(s.StaticURL, http.StripPrefix(s.StaticURL, http.FileServer(http.Dir(s.StaticFolder))))
			utils.Debug("add static route : %s", s.StaticURL)
		}

	}

	if s.IndexTemp != nil {
		http.HandleFunc("/", s.home)
	}
	// http.HandleFunc("/"+s.WsURL, s.ws)

	var addr = s.Addr
	if addr == "" {
		if s.WsConfig.HTTPSCertFile != "" && s.WsConfig.HTTPSKeyFile != "" {
			addr = ":443"
		} else {
			addr = ":80"
		}
	}
	utils.Info("ListenAndServe : %s", addr)

	if s.WsConfig.HTTPSCertFile != "" && s.WsConfig.HTTPSKeyFile != "" {
		err := http.ListenAndServeTLS(addr, s.WsConfig.HTTPSCertFile, s.WsConfig.HTTPSKeyFile, http.HandlerFunc(s.beforeRoute))
		if err != nil {
			utils.Error("ListenAndServe Error: %s", err)
		}
	} else {
		err := http.ListenAndServe(addr, http.HandlerFunc(s.beforeRoute))
		if err != nil {
			utils.Error("ListenAndServe Error: %s", err)
		}
	}
}

func (s *Server) beforeRoute(w http.ResponseWriter, req *http.Request) {
	if !utils.BeginWith(req.URL.Path, s.StaticURL) {
		req.URL.Path = strings.ToLower(req.URL.Path)
	}
	http.DefaultServeMux.ServeHTTP(w, req)
}

func (s *Server) Route(name string, route http.Handler) {
	http.Handle(name, route)
}

func (s *Server) home(w http.ResponseWriter, r *http.Request) {

	// if r.URL.Path != "/" {
	// 	http.Error(w, "Not found", http.StatusNotFound)
	// 	return
	// }
	// if r.Method != "GET" {
	// 	http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
	// 	return
	// }

	// r.ParseForm()
	// authid := r.Form.Get("auth")
	// wsUrl := s.WsURL
	// if len(authid) > 0 {
	// 	wsUrl = wsUrl + "?auth=" + authid
	// }
	// r.Form["auth"] = []string{authid}

	// r.Form["WsURL"] = []string{wsUrl}

	// var param = make(map[string]interface{})
	// param["WsURL"] = s.WsURL
	// for name := range r.Form {
	// 	param[name] = r.Form.Get(name)
	// }

	// t, err := template.ParseFiles(s.IndexTemp...)

	// if err != nil {
	// 	utils.Error("parse index file err:", err)
	// } else {
	// 	s.IndexTemp = t
	// }

	s.IndexTemp.ExecuteTemplate(w, "index", nil)
	// http.ServeFile(w, r, "index.html")
}

func (s *Server) Ws(w http.ResponseWriter, r *http.Request, key string, clientIP string) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		utils.Error("upgrade fail : %s", err)
		return
	}

	wsConn := BuilderInstance().BuildConn(conn, s, key, clientIP)

	go wsConn.ReadProc()
	go wsConn.WriteProc()
	s.muConns.Lock()
	s.Conns = append(s.Conns, wsConn)
	s.muConns.Unlock()
	utils.Info("new conn , count : %d", s.ConnCount())
}

// ConnCount 连接数
func (s *Server) ConnCount() int {
	s.muConns.RLock()
	defer s.muConns.RUnlock()
	return len(s.Conns)
}

// 根据id获取连接
func (s *Server) GetConn(key string) *Conn {
	s.muConns.RLock()
	defer s.muConns.RUnlock()
	for _, c := range s.Conns {
		if c.ID == key {
			return c
		}
	}
	return nil
}

// ChannelCount 频道数
func (s *Server) ChannelCount() int {
	s.muChannels.RLock()
	defer s.muChannels.RUnlock()
	return len(s.Channels)
}

// EnterChannel 设置连接的频道
func (s *Server) EnterChannel(conn *Conn, channel string) {
	s.muChannels.Lock()
	defer s.muChannels.Unlock()

	var c *Channel
	var ok bool
	if c, ok = s.Channels[channel]; ok {
		c.Add(conn)
		return
	} else {
		c := BuilderInstance().BuilderChannel(channel)
		if nil != s.OnCreateChannel {
			s.OnCreateChannel(c)
		}
		if c.DeleteChannelWhenNoConn {
			c.serverDeleteChannel = s.DeleteChannel
		}
		c.Add(conn)
		s.Channels[channel] = c
	}
}

// QuitChannel 连接退出频道
func (s *Server) QuitChannel(conn *Conn, channel string) {
	s.muChannels.RLock()
	defer s.muChannels.RUnlock()

	if c, ok := s.Channels[channel]; ok {
		c.Delete(conn)
	}

}

// QuitAllChannel 退出所有频道
func (s *Server) QuitAllChannel(conn *Conn) {
	s.muChannels.RLock()
	defer s.muChannels.RUnlock()

	for _, c := range s.Channels {
		c.Delete(conn)
	}
}

// DeleteConn 删除连接
func (s *Server) DeleteConn(conn *Conn) {
	s.QuitAllChannel(conn)
	s.muConns.Lock()
	defer s.muConns.Unlock()

	for i, c := range s.Conns {
		if conn == c {
			s.Conns = append(s.Conns[:i], s.Conns[i+1:]...)
			break
		}
	}

	if s.OnClose != nil {
		s.OnClose(conn)
	}
}

// DeleteChannel 删除频道
func (s *Server) DeleteChannel(channelName string) {
	s.muChannels.Lock()
	defer s.muChannels.Unlock()

	if c, ok := s.Channels[channelName]; ok {
		delete(s.Channels, channelName)
		s.OnChannelDeleted(c)
		c.Exit()
	}
}

// ChannelList 频道列表
func (s *Server) ChannelList() []string {
	s.muChannels.RLock()
	defer s.muChannels.RUnlock()
	var ret []string
	for c := range s.Channels {
		ret = append(ret, c)
	}
	return ret
}

// HasChannel 判断是否存在该频道
func (s *Server) HasChannel(channelName string) bool {
	s.muChannels.RLock()
	defer s.muChannels.RUnlock()
	if _, ok := s.Channels[channelName]; ok {
		return true
	}
	return false
}

// GetChannel 获取频道
func (s *Server) GetChannel(channelname string) *Channel {

	s.muChannels.RLock()
	defer s.muChannels.RUnlock()
	if c, ok := s.Channels[channelname]; ok {
		return c
	}
	return nil
}

// GetChannelsByConn 获取连接所属的频道列表
func (s *Server) GetChannelsByConn(conn *Conn) []*Channel {
	s.muChannels.RLock()
	defer s.muChannels.RUnlock()
	var ret []*Channel
	for _, c := range s.Channels {
		if c.Has(conn) {
			ret = append(ret, c)
		}
	}
	return ret
}

// SendToChannel 将数据发给频道
func (s *Server) SendToChannel(channelName string, msg []byte) {
	channel := s.GetChannel(channelName)
	if nil == channel {
		if nil != s.OnChannelNotExists {
			s.OnChannelNotExists(channelName)
		}
		return
	}
	channel.Broadcast(msg)
}

// Broadcast 全局广播
func (s *Server) Broadcast(msg []byte) {
	s.muConns.RLock()
	defer s.muConns.RUnlock()
	for _, conn := range s.Conns {
		conn.writeBufferChan <- msg
	}
}
